package xyz.scootaloo.kami.server.service

import io.vertx.core.Future
import xyz.scootaloo.kami.server.model.AccessToken
import xyz.scootaloo.kami.server.model.AccessType
import xyz.scootaloo.kami.server.model.SysRule
import xyz.scootaloo.kami.server.model.accessDenied
import xyz.scootaloo.kami.server.standard.AccessDeniedException
import xyz.scootaloo.kami.server.standard.InternalApi
import xyz.scootaloo.kami.server.standard.UploadExceedLimitException
import xyz.scootaloo.kami.server.service.impl.InternalRuleSystemServiceImpl

/**
 * @author flutterdash@qq.com
 * @since 2022/3/23 20:05
 */
interface RuleSystemService {

    companion object {
        operator fun invoke(): RuleSystemService {
            return InternalRuleSystemServiceImpl
        }
    }

    /**
     * - 模拟用户进入文件系统. 这个方法不会真正访问文件系统, 而是使用数据库提供的信息去尝试在系统内部维护的
     * 前缀树上找一个对应的节点, 并把这个节点返回.
     *
     * - 这个系统的主要数据结构是前缀树, 和文件系统的文件树结构很类似, 但是它是由数据库中数据表的记录[SysRule]生成的,
     * 在当前实现中, 系统会根据这些数据库中的所有规则生成一个前缀树, 其中树的每一个节点对应文件系统的文件(或目录),
     * 如果这个节点是目录, 那么它还可能有子节点.
     *
     * - **在搜索目标节点的过程中, 如果经过一个节点, 而用户的等级低于此节点中此操作要求的最低等级, 那么访问会被拒绝**,
     * 同时抛出一个[AccessDeniedException]异常
     *
     * - 假如在寻找目标节点的过程中, 没有被拒绝访问, 则返回于这个路径匹配的规则[RuleItem]对象;
     * 可以使用这个对象进行其他操作, 例如给指定的用户查看目录中允许访问的内容,
     * 使用从文件系统中实际获取到的文件列表和[RuleItem.subItem]中的子项目进行依次比较, 过滤掉其中权限不足的项目,
     * 具体用法可以参考[FileSystemViewService]中的实现细节
     *
     * @param token 包含用户的权限, 操作类型和要访问的路径
     * @param block 一个回调, 当经过计算得到了[SentryNode]节点对象后, 交给回调处理,
     * ***警告: 这个参数[SentryNode]只能在回调用使用, 不允许发布到其他地方, 否则会产生线程安全问题***
     */
    fun <T> access(token: AccessToken, block: (RuleItem) -> T): Future<T>
    fun <T> accessAsync(token: AccessToken, block: (RuleItem) -> Future<T>): Future<T>
    suspend fun <T> accessWithCoroutine(token: AccessToken, block: suspend (RuleItem) -> T): T
    fun access(token: AccessToken): Future<Unit> = access(token) {}

    @Throws(UploadExceedLimitException::class)
    @InternalApi fun tryShrinkageCap(path: String, amount: Long): Future<Unit>
    @InternalApi fun releaseCap(path: String, amount: Long): Future<Unit>

    fun createRule(token: AccessToken, rule: ByteArray): Future<Unit>
    fun updateRule(token: AccessToken, rule: ByteArray): Future<Unit>
    fun deleteRule(token: AccessToken): Future<Unit>

    fun showRule(path: String): Future<LowRuleItem>
    fun showCap(path: String): Future<LowRuleItem>
    fun deleteLimit(token: AccessToken): Future<Unit>
    fun createCapLimit(token: AccessToken, limit: Long): Future<Unit>
    @InternalApi fun updateCapAvailable(path: String, used: Long): Future<Unit>

    @InternalApi suspend fun listCapLimitPaths(): Future<List<String>>

    interface LowRuleItem {
        fun fullPath(): String
        fun created(): Long
        fun updated(): Long
        fun rule(): ByteArray
        fun hasRuleRestrict(): Boolean
        fun hasCapacityRestrict(): Boolean
        fun capacityLimit(): Long
        fun capacityAvailable(): Long
        fun isAllow(userLevel: Int, type: AccessType): Boolean
        fun assertAllow(userLevel: Int, type: AccessType) {
            if (!isAllow(userLevel, type))
                throw accessDenied(type)
        }
    }

    interface RuleItem : LowRuleItem {
        fun containsSubItem(itemKey: String): Boolean
        fun subItem(itemKey: String): LowRuleItem
    }
}